package com.superhelper.deliver.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import com.superhelper.common.Consts;
import com.superhelper.common.domain.AuserShop;
import com.superhelper.common.domain.DeliverAccount;
import com.superhelper.common.domain.DeliverRecord;
import com.superhelper.common.domain.DeliverRecordStatus;
import com.superhelper.common.domain.ExtMsg;
import com.superhelper.common.domain.LoginLog;
import com.superhelper.common.domain.PushMsgType;
import com.superhelper.common.domain.TakeOutOrder;
import com.superhelper.common.domain.TakeOutOrderStatus;
import com.superhelper.common.domain.TakeOutOrderType;
import com.superhelper.common.domain.Task;
import com.superhelper.common.mapper.DeliverRecordMapper;
import com.superhelper.common.mapper.TakeOutOrderMapper;
import com.superhelper.common.mapper.TaskMapper;
import com.superhelper.common.service.PushService;
import com.superhelper.common.service.RedisService;
import com.superhelper.common.utils.HttpClientHelper;
import com.superhelper.common.utils.JsonHelper;
import com.superhelper.common.utils.JsonObjectUtil;

@Service
public class DaDaDeliverService implements DeliverService {
    private static final Logger log = LoggerFactory.getLogger(DaDaDeliverService.class);

    @Autowired
    private TakeOutOrderMapper orderMapper;

    @Autowired
    private DeliverRecordMapper recordMapper;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private RedisService redis;

    @Value("${dada.key}")
    private String appKey;
    @Value("${dada.secret}")
    private String appSecret;

    @Value("${dada.host}")
    private String host;

    @Value("${dada.callback}")
    private String callback;

    private static final String QUERY = "/api/order/queryDeliverFee";

    private static final String ADD = "/api/order/addAfterQuery";

    private static final String ADD_ORDER = "/api/order/addOrder";
    private static final String ADD_TIP = "/api/order/addTip";

    private static final String QUERY_ORDER = "/api/order/status/query";

    @Autowired
    private HttpClientHelper httpClient;

    @Autowired
    protected PushService pushService;

    /**
     * 返回true表示处理成功
     */
    @Override
    public boolean deliver(DeliverRecord record) {
        if (record.getStatus() > DeliverRecordStatus.unsend.getVal()) {
            log.info("has deliver, orderId:{},status:{}", record.getOrderId(), record.getStatus());
            return true;
        }
        TakeOutOrder order = orderMapper.getById(record.getOrderId());
        if (StringUtils.equalsIgnoreCase(order.getStatus(), TakeOutOrderStatus.INVALID.getValue())) {
            log.info("order is invalid,not need to deliver, orderId:{}", record.getOrderId());
            return true;
        }
        try {
            Map<String, Object> resultMap = null;
            AuserShop shop = this.getShop(order.getShopId(), order.getType());
            String deliverNo = buildDeliverNo(record);
            // 调用查询接口
            resultMap = addOrder(order, shop, deliverNo, record);
            if (resultMap == null) {
                return false;
            }
            // 如果是空的，则删除发单任务，会有查单任务继续接下来的流程
            if (resultMap.isEmpty()) {
                log.info("wait query task deliverRecordId:{},orderId:{}", record.getId(), record.getOrderId());
                return true;
            }
            Object distObj = resultMap.get("distance");
            if (distObj == null) {
                return false;
            }
            record.setDeliverFee(MapUtils.getDouble(resultMap, "fee"));
            record.setDeliverNo(deliverNo);
            record.setDeliverPlf("dada");
            record.setStatus(DeliverRecordStatus.waiting.getVal());
            recordMapper.updateDeliver(record);
            return true;
        } catch (Exception e) {
            log.error("error", e);
        }
        return false;
    }

    /**
     * 返回true表示处理成功
     */
    @Override
    public boolean query(DeliverRecord record) {
        TakeOutOrder order = orderMapper.getById(record.getOrderId());
        try {
            Map<String, Object> resultMap = null;
            AuserShop shop = this.getShop(order.getShopId(), order.getType());
            String deliverNo = record.getId() + "";
            // 调用查询接口
            resultMap = queryOrder(order, shop, record);
            if (resultMap == null) {
                return false;
            }
            // 如果没有内容，不需要再次查询，比如是订单不存在
            if (resultMap.size() == 0) {
                // 那么需要增加一个发单任务
                taskMapper.addTask(new Task(Consts.Task.deliver_record, record.getId() + ""));
                log.info("query no order! addTask to deliver,deliverRecordId:{},orderId:{}", record.getId(),
                        record.getOrderId());
                return true;
            }
            record.setDeliverFee(MapUtils.getDouble(resultMap, "deliveryFee"));
            record.setDeliverNo(deliverNo);
            record.setDeliverPlf("dada");
            record.setTransporterName(MapUtils.getString(resultMap, "transporterName"));
            record.setTransporterPhone(MapUtils.getString(resultMap, "transporterPhone"));
            record.setStatus(MapUtils.getInteger(resultMap, "statusCode"));
            record.setStatusMsg(MapUtils.getString(resultMap, "statusMsg"));

            if (record.getStatus() != null) {
                recordMapper.updateDeliver(record);
            }
            return true;
        } catch (Exception e) {
            log.error("error", e);
        }
        return false;
    }

    /**
     * 返回true表示处理成功
     */
    public boolean deliver2(DeliverRecord record) {
        if (record.getStatus() > DeliverRecordStatus.unsend.getVal()) {
            log.info("has deliver, orderId:{},status:{}", record.getOrderId(), record.getStatus());
            return true;
        }
        TakeOutOrder order = orderMapper.getById(record.getOrderId());
        try {
            Map<String, Object> resultMap = null;
            AuserShop shop = this.getShop(order.getShopId(), order.getType());
            // 调用查询接口
            resultMap = query(order, shop);
            if (resultMap == null) {
                return false;
            }
            Object distObj = resultMap.get("distance");
            if (distObj == null) {
                return false;
            }
            double dist = JsonObjectUtil.getDouble(distObj);
            // 如果距离大于1公里，距离有问题
            if (Math.abs(dist - order.getDist()) > 1) {
                recordMapper.updateStatus(record.getId(), DeliverRecordStatus.abnormal.getVal(),
                        "距离不相符，达达显示为" + dist + "，超过系统计算的" + order.getDist() + "一公里多");
                return true;
            }
            record.setDeliverFee(MapUtils.getDouble(resultMap, "fee"));
            record.setDeliverNo(MapUtils.getString(resultMap, "deliveryNo"));
            record.setDeliverPlf("dada");
            recordMapper.updateDeliver(record);
            return toDelivery(resultMap, order, shop, record);
        } catch (Exception e) {
            log.error("error", e);
        }
        return false;
    }

    private boolean toDelivery(Map<String, Object> resultMap, TakeOutOrder order, AuserShop shop, DeliverRecord dr)
            throws Exception {
        String deliverNo = MapUtils.getString(resultMap, "deliveryNo");
        // 添加一条记录到DeliveryRecord

        String uri = host + ADD;
        String body = buildAddBody(deliverNo);
        String req = buildRequest(body, order, shop);
        String resp = httpClient.doPost(uri, req);
        log.info("request:{},response:{}", req, resp);
        if (StringUtils.isNotBlank(resp)) {
            @SuppressWarnings("unchecked")
            Map<String, Object> map = JsonHelper.fromJson(resp, Map.class);
            if (0 == (Integer) map.get("code")) {
                recordMapper.updateStatusWait(dr.getId(), "发单成功");
                return true;
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> addOrder(TakeOutOrder order, AuserShop shop, String deliverNo, DeliverRecord record)
            throws Exception {
        String uri = host + ADD_ORDER;
        String body = buildAddOrderBody(order, shop, deliverNo);
        String req = buildRequest(body, order, shop);
        try {
            String resp = httpClient.doPost(uri, req);
            log.info("[addOrder] request:{},response:{}", req, resp);
            if (StringUtils.isNotBlank(resp)) {
                Map<String, Object> result = JsonHelper.fromJson(resp, Map.class);
                int code = (Integer) result.get("code");
                if (0 == code) {
                    return (Map<String, Object>) result.get("result");
                }
                if (2155 == code) {
                    publish(order, PushMsgType.DELIVER_MONEY_EXCEPTION);
                }
            }
        } catch (Exception e) {
            log.error("addOrder deliverNo:{} error", deliverNo, e);
            Task task = new Task(Consts.Task.query_deliver_record, record.getId() + "");
            // 10秒后去查询
            task.setNextTime(new Date(task.getCreateTime().getTime() + 30000));
            taskMapper.addTask(task);
            return Collections.emptyMap();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> queryOrder(TakeOutOrder order, AuserShop shop, DeliverRecord record) throws Exception {
        String uri = host + QUERY_ORDER;
        String body = buildQueryOrderBody(record);
        String req = buildRequest(body, order, shop);
        String resp = httpClient.doPost(uri, req);
        log.info("[queryOrder] request:{},response:{}", req, resp);
        if (StringUtils.isNotBlank(resp)) {
            Map<String, Object> result = JsonHelper.fromJson(resp, Map.class);
            int code = (Integer) result.get("code");
            if (0 == code) {
                return (Map<String, Object>) result.get("result");
            }
            if (2005 == code) {
                return Collections.emptyMap();
            }
        }
        return null;
    }

    public Long publish(TakeOutOrder order, PushMsgType msgType) {
        Map<String, Object> msgMap = new HashMap<>();
        msgMap.put("orderId", order.getId());
        msgMap.put("type", msgType.getValue());
        msgMap.put("book", order.getBook());
        msgMap.put("shop_type", order.getType());
        msgMap.put("retry", false);
        AuserShop shop = this.getShop(order.getShopId(), order.getType());
        // pc端是否在线
        String online_key = String.format(Consts.Redis.USER_ONLINE, shop.getAuserId());
        Map user = redis.get(online_key, Map.class);
        log.info("onlineKey,value:{}", user);
        if (user != null) {// pc
            String key = String.format(Consts.Redis.SUB_ORDER, order.getType(), order.getShopId());
            String msg = JsonHelper.toJson(msgMap);
            redis.listPushTail(key, msg);
            log.info("publish key:{},value:{}", key, msg);
        }

        // app推送
        String title = "您有新消息";
        ExtMsg extras = new ExtMsg("order", msgMap);
        // 最后登录的信息
        String logKey = Consts.Redis.USER_LOGIN_PREFIX + shop.getAuserId();
        LoginLog userLog = redis.get(logKey, LoginLog.class);
        if (userLog != null) {
            pushService.push(title, title, title, userLog.getImid(), userLog.getDevice(), extras);
            log.info("publish imid:{},value:{}", userLog.getImid(), msgMap);
        }

        return 1L;
    }

    @SuppressWarnings("unchecked")
    private boolean addTip(TakeOutOrder order, DeliverRecord record, AuserShop shop) throws Exception {
        String uri = host + ADD_TIP;
        String body = buildAddTipBody(record);
        String req = buildRequest(body, order, shop);
        String resp = httpClient.doPost(uri, req);
        log.info("[addTip] request:{},response:{}", req, resp);
        if (StringUtils.isNotBlank(resp)) {
            Map<String, Object> result = JsonHelper.fromJson(resp, Map.class);

            int code = (Integer) result.get("code");

            if (0 == code) {
                return true;
            }
            // {"status":"fail","errorCode":2058,"code":2058,"msg":"小费不能减少"}
            if (2058 == code) {
                return true;
            }
            if (code == 2059) {
                taskMapper.addTask(new Task(Consts.Task.query_deliver_record, record.getId() + ""));
                return true;
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> query(TakeOutOrder order, AuserShop shop) throws Exception {
        String uri = host + QUERY;
        String body = buildQueryBody(order);
        String req = buildRequest(body, order, shop);
        String resp = httpClient.doPost(uri, req);
        log.info("request:{},response:{}", req, resp);
        if (StringUtils.isNotBlank(resp)) {
            Map<String, Object> result = JsonHelper.fromJson(resp, Map.class);
            if (0 == (Integer) result.get("code")) {
                return (Map<String, Object>) result.get("result");
            }
        }
        return null;
    }

    private String buildRequest(String body, TakeOutOrder order, AuserShop shop) {
        Map<String, Object> param = new HashMap<>();
        param.put("app_key", appKey);
        param.put("timestamp", System.currentTimeMillis() / 1000);
        param.put("format", "json");
        param.put("v", "1.0");
        param.put("source_id", shop.getDeliverUid());// order.getShopId());
        param.put("body", body);
        param.put("signature", getSign(param));
        return JsonHelper.toJson(param);
    }

    private String buildQueryBody(TakeOutOrder order) {
        Map<String, Object> param = new HashMap<>();
        param.put("origin_id", order.getId() + "_1");
        param.put("shop_no", order.getShopId());
        param.put("city_code", "020");
        param.put("cargo_price", order.getTotalPrice());
        param.put("is_prepay", 1);
        param.put("expected_fetch_time", new Date().getTime() / 1000);
        param.put("receiver_name", order.getConsigness());
        param.put("receiver_address", order.getDeliverPoi());
        param.put("receiver_phone", order.getPhone());
        param.put("receiver_lat", order.getDeliverLat());
        param.put("receiver_lng", order.getDeliverLon());
        param.put("tips", 0d);
        param.put("cargo_type", 1);
        param.put("callback", callback);
        return JsonHelper.toJson(param);
    }

    private String buildAddOrderBody(TakeOutOrder order, AuserShop shop, String deliverNo) {
        Map<String, Object> param = new HashMap<>();
        param.put("origin_id", deliverNo);
        param.put("shop_no", shop.getDeliverShopNo());
        param.put("city_code", "020");
        param.put("cargo_price", order.getTotalPrice());
        param.put("is_prepay", 0);
        param.put("expected_fetch_time", new Date().getTime() / 1000 + 600);
        param.put("receiver_name", order.getConsigness());
        param.put("receiver_address", order.getDeliverPoi());
        param.put("receiver_phone", order.getPhone());
        param.put("receiver_lat", order.getDeliverLat());
        param.put("receiver_lng", order.getDeliverLon());
        param.put("tips", 0d);
        param.put("cargo_type", 1);
        param.put("info", TakeOutOrderType.get(order.getType()).getDesc() + "序号：" + order.getDaySn());

        param.put("origin_mark", "ele".equals(order.getType()) ? "elm" : order.getType());
        param.put("origin_mark_no", order.getDaySn());

        param.put("callback", callback);
        return JsonHelper.toJson(param);
    }

    private String buildAddTipBody(DeliverRecord record) {
        Map<String, Object> param = new HashMap<>();
        param.put("order_id", record.getDeliverNo());
        param.put("tips", record.getCurrentTip());
        param.put("city_code", "020");
        param.put("info", "亲，请帮帮我！");
        return JsonHelper.toJson(param);
    }

    private String buildQueryOrderBody(DeliverRecord record) {
        Map<String, Object> param = new HashMap<>();
        param.put("order_id", buildDeliverNo(record));
        return JsonHelper.toJson(param);
    }

    private String buildAddBody(String deliveryNo) {
        Map<String, Object> param = new HashMap<>();
        param.put("deliveryNo", deliveryNo);
        return JsonHelper.toJson(param);
    }

    private String getSign(Map<String, Object> requestMap) {
        // Key值排序
        Collection<String> keySet = requestMap.keySet();
        List<String> list = new ArrayList<String>(keySet);
        Collections.sort(list);

        // 拼凑签名字符串
        StringBuffer signStr = new StringBuffer();
        for (int i = 0; i < list.size(); i++) {
            String key = list.get(i);
            signStr.append(key + requestMap.get(key));
        }
        // MD5签名
        String mySign = DigestUtils
                .md5DigestAsHex((appSecret + signStr.toString() + appSecret).getBytes(Consts.UTF8_CHARSET));
        String sign = mySign.toUpperCase();
        return sign;
    }

    private String buildDeliverNo(DeliverRecord record) {
        return record.getId() + "";
    }

    public static void main(String[] args) throws Exception {
        String str = "appSecretapp_keyapp_keybody{\"order_id\":\"20170301000001\"}formatjsonsource_id73753timestamp1488363493v1.0appSecret";
        System.out.println(new String(DigestUtils.md5DigestAsHex(str.getBytes("utf-8"))));
    }

    @Override
    public boolean addTip(DeliverRecord record) {
        if (record.getStatus() > DeliverRecordStatus.waiting.getVal()) {
            log.info("addTip status is incorrect, orderId:{},status:{}", record.getOrderId(), record.getStatus());
            return true;
        }
        TakeOutOrder order = orderMapper.getById(record.getOrderId());
        try {
            record.setCurrentTip(record.getCurrentTip() + record.getPerTip());
            record.setAddTipTimes(record.getAddTipTimes() + 1);
            // 调用查询接口
            AuserShop shop = this.getShop(order.getShopId(), order.getType());
            boolean addResult = addTip(order, record, shop);
            if (addResult) {
                recordMapper.updateAfterAddTip(record);
            }
            return addResult;
        } catch (Exception e) {
            log.error("error", e);
        }
        return false;
    }

    private AuserShop getShop(String shopId, String type) {
        AuserShop shop = orderMapper.getShop(shopId, type);
        DeliverAccount deliverAccount = orderMapper.getDeliverAccountByType(Long.valueOf(shop.getAuserId()),
                shop.getDeliverType());
        if (deliverAccount != null) {
            shop.setDeliverUid(deliverAccount.getDeliverUid());
            shop.setDeliverShopNo(deliverAccount.getDeliverShopNo());
        }
        return shop;
    }
}
